import os
import time
import logging
import logging.handlers
from datetime import datetime


class SizeAndTimeRotatingFileHandler(logging.handlers.RotatingFileHandler):
    """
    自定义日志 Handler，支持：
    - 按文件大小滚动 (类似 RotatingFileHandler)
    - 按时间滚动（如每天）(类似 TimedRotatingFileHandler)

    参数说明：
    - filename: 日志文件名
    - maxBytes: 每个日志文件的最大字节数，超过则滚动 (类似 RotatingFileHandler)
    - backupCount: 保留的备份文件数
    - when: 时间间隔单位，如 'midnight' 表示每天午夜 (类似 TimedRotatingFileHandler)
    - interval: 时间间隔数字，如 when='D', interval=1 表示每天
    - encoding: 文件编码
    - delay: 是否延迟打开文件
    - utc: 是否使用 UTC 时间
    """

    def __init__(self, filename, maxBytes=0, backupCount=0,  # noqa
                 when='midnight', interval=1, encoding=None, delay=False, utc=False):
        # 初始化父类 RotatingFileHandler（用于处理 size）
        super().__init__(filename, maxBytes=maxBytes, backupCount=backupCount,
                         encoding=encoding, delay=delay)

        # 添加时间相关的属性
        self.when = when.upper()
        self.interval = interval
        self.utc = utc
        self.rolloverAt = self.computeRollover(time.time())

    def computeRollover(self, currentTime):  # noqa
        """
        计算下一次按时间滚动的时刻（仿照 TimedRotatingFileHandler 的逻辑）
        """
        result = None
        if self.when == 'MIDNIGHT' or self.when.startswith('W'):
            # 简化起见，这里只处理 'midnight' 情况，即每天午夜滚动一次
            if self.when == 'MIDNIGHT':
                t = time.localtime(currentTime)
                # 当前日的 0 点
                result = time.mktime((
                    t.tm_year, t.tm_mon, t.tm_mday,
                    0, 0, 0, t.tm_wday, t.tm_yday, t.tm_isdst
                )) + 86400  # 下一个 0 点（明天 0 点）
                if self.interval > 1:
                    result += (self.interval - 1) * 86400
        else:
            # 可以扩展支持 'S', 'M', 'H', 'D', 'W0'-'W6' 等，但为了简化，这里只处理 'MIDNIGHT'
            raise ValueError(f"Unsupported when value: {self.when}")

        return result

    def shouldRollover(self, record):
        """
        判断是否应该滚动日志文件：
        如果文件大小超过 maxBytes 或者当前时间超过了计划的滚动时间，则返回 True
        """
        # 先检查文件大小是否超出
        if self.stream is None:
            self.stream = self._open()
        if self.maxBytes > 0:
            msg = "%s\n" % self.format(record)
            self.stream.seek(0, 2)  # 移动到文件末尾
            if self.stream.tell() + len(msg.encode(self.encoding or 'utf-8')) >= self.maxBytes:
                return True

        # 再检查时间是否到达
        t = time.time()
        if t >= self.rolloverAt:
            return True

        return False

    def doRollover(self):
        """
        执行日志滚动操作：
        1. 关闭当前流
        2. 按时间生成新的文件名（如加上日期）
        3. 重新打开新文件
        4. 清理过多的历史文件
        """
        if self.stream:
            self.stream.close()
            self.stream = None

        # 按时间生成新的文件名，比如追加日期
        current_time = time.strftime("%Y-%m-%d", time.localtime())
        dir_name, base_name = os.path.split(self.baseFilename)
        name_part, ext_part = os.path.splitext(base_name)

        # 新的日志文件名，例如：app.log -> app_2024-06-01.log
        new_filename = f"{name_part}_{current_time}{ext_part}"
        new_filepath = os.path.join(dir_name, new_filename)

        # 如果新文件名与当前文件不同，则重命名/移动当前日志文件
        if os.path.abspath(self.baseFilename) != os.path.abspath(new_filepath):
            if os.path.exists(self.baseFilename):
                if os.path.exists(new_filepath):
                    os.remove(new_filepath)  # 删除已存在的，避免冲突
                os.rename(self.baseFilename, new_filepath)

        # 更新 self.baseFilename 为新的文件路径（可选，视需求而定）
        # 如果你希望后续日志继续写入带日期的文件，可以修改 self.baseFilename
        # 但这样会导致无法继续按大小滚动。所以更常见的做法是：
        # 每天一个固定命名的日志文件（如 app_2024-06-01.log），然后每个文件再按大小分隔

        # 这里我们选择：每天一个新文件（带日期），每个文件内部再按大小分隔
        # 所以我们将 self.baseFilename 指向新文件
        self.baseFilename = new_filepath

        # 重新打开文件
        self.mode = 'a'
        self.stream = self._open()

        # 计算下次滚动时间
        currentTime = time.time()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval * 86400  # 假设 when='MIDNIGHT', interval=1
        self.rolloverAt = newRolloverAt

        # 注意：本类没有实现类似 TimedRotatingFileHandler 的备份文件删除逻辑，
        # 如需删除旧日志，可以自己添加清理逻辑，比如按日期删除超过 backupCount 天的日志文件

    def emit(self, record):
        """
        重写 emit，加入 shouldRollover 判断
        """
        # noinspection PyBroadException
        try:
            if self.shouldRollover(record):
                self.doRollover()
            super().emit(record)
        except Exception as e:
            self.handleError(record)


if __name__ == '__main__':
    pass
